package com.springmvc.context;

import com.springmvc.annotation.*;
import com.springmvc.exception.ContextException;
import com.springmvc.xml.XmlParser;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by DELL on 2022/4/2
 * Spring容器
 **/
public class WebApplicationContext {

    String contextConfigLocation;
    public List<String> classNameList = new ArrayList<>();
    //spring的ioc容器
    public Map<String, Object> iocMap = new ConcurrentHashMap<String, Object>();

    public WebApplicationContext(String contextConfigLocation) {
        this.contextConfigLocation = contextConfigLocation;
    }

    /**
     * 初始化spring容器
     */
    public void refresh() {
        String basePackage = XmlParser.getBasePackage(contextConfigLocation.split(":")[1]);
        String[] basePackages = basePackage.split(",");
        if (basePackages.length > 0) {
            for (String pack : basePackages) {
                excuteScanPackage(pack);
            }
        }
        System.out.println("扫描之后的内容是: " + classNameList);
        //实例化spring容器中的bean
        excuteInstance();
        //IOC容器中的对象
        System.out.println("Spring的IOC容器对象是: " + iocMap);
        //事项spring容器中对象的注入
        excuteAutowired();
    }

    /**
     * 扫描包
     *
     * @param pack
     */
    public void excuteScanPackage(String pack) {
        // 正则表达式\\.表示编译器先将字符串转变为“正则表达式”， \.又被正则表达式引擎解释为.
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
        System.out.println("url: " + url);
        String file = url.getFile();
        File dir = new File(file);
        for (File f : dir.listFiles()) {
            if (f.isDirectory()) {
                //如果是文件夹
                excuteScanPackage(pack + "." + f.getName());
            } else {
                String className = pack + "." + f.getName().replaceAll(".class", "");
                classNameList.add(className);
            }
        }

    }

    /**
     * 实例化spring容器中bean对象
     */
    public void excuteInstance() {
        if (classNameList.size() == 0) {
            //没有扫描到需要实例化的类
            throw new ContextException("没有要实例化的bean");
        }
        for (String className : classNameList) {
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(Controller.class)) {
                    //com.caihappy.controller.UserController
                    //控制层对象名字userController
                    String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
                    iocMap.put(beanName, clazz.newInstance());
                } else if (clazz.isAnnotationPresent(Service.class)) {
                    //com.caihappy.service.impl.UserServiceImpl
                    Service serviceAnnotation = clazz.getAnnotation(Service.class);
                    //查看注解是否设置value
                    String beanName = serviceAnnotation.value();
                    if ("".equals(beanName)) {
                        Class<?>[] interfaces = clazz.getInterfaces();
                        for (Class<?> c : interfaces) {
                            System.out.println(c.getSimpleName());
                            beanName = c.getSimpleName().substring(0, 1).toLowerCase() + c.getSimpleName().substring(1);
                            iocMap.put(beanName, clazz.newInstance());
                        }
                    } else {
                        iocMap.put(beanName, clazz.newInstance());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 事项Spring容器中的对象的依赖注入
     */
    public void excuteAutowired() {
        if (iocMap.isEmpty()) {
            throw new ContextException("没有找到初始化的bean对象");
        }
        try {
            for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
                String key = entry.getKey();
                Object bean = entry.getValue();
                Field[] declaredFields = bean.getClass().getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    if (declaredField.isAnnotationPresent(Autowired.class)) {
                        Autowired annotation = declaredField.getAnnotation(Autowired.class);
                        String beanName = annotation.value();
                        if ("".equals(beanName)) {
                            Class<?> type = declaredField.getType();
                            beanName = type.getSimpleName().substring(0, 1).toLowerCase() + type.getSimpleName().substring(1);
                        }
                        /**
                         * 将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
                         * 值为 false 则指示反射的对象应该实施 Java 语言访问检查;实际上setAccessible是启用和禁用访问安全检查的开关,
                         * 并不是为true就能访问为false就不能访问 ；
                         * 由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
                         */
                        //设置取消java访问检查机制，true为取消访问检测，例如设置为私有的属性也能获取到
                        declaredField.setAccessible(true);
                        //属性注入，调用反射，给属性赋值
                        declaredField.set(bean, iocMap.get(beanName));
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


}
